package unity.messenger.security;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import unity.messenger.security.exceptions.KeyPairGenerationException;
import unity.messenger.security.exceptions.KeyPairGenerationException.Error;

public class KeyPair
{
	public static final String RSA_ALG_PREFIX = Algorithm.RSA_512.toString().split("_")[0];
	public static final String ECDSA_ALG_PREFIX = Algorithm.ECDSA_512.toString().split("_")[0];
	
	public static enum Algorithm
	{
		RSA ("RSA"),
		ECDSA ("ECDSA"),
		RSA_512 ("RSA", 512),
		RSA_1024 ("RSA", 1024),
		RSA_2048 ("RSA", 2048),
		RSA_4096 ("RSA", 4096),
		ECDSA_192 ("ECDSA", 192),
		ECDSA_256 ("ECDSA", 256),
		ECDSA_384 ("ECDSA", 384),
		ECDSA_512 ("ECDSA", 512);
		
		private String name;
		private int keysize;
		
		private Algorithm(String name, int keysize)
		{
			this.name = name;
			this.keysize = keysize;
		}
		
		private Algorithm(String name)
		{
			this.name = name;
		}
		
		public int getKeySize()
		{
			if(this.keysize >= 0) return this.keysize;
			else throw new IllegalAccessError("The selected algorithm doesn't have a defined key size. It must be used only for reference");
		}
		
		public String getName()
		{
			return this.name;
		}
	}
	
	private PrivateKey privateKey;
	private PublicKey publicKey;
	
	private Algorithm algorithm;

	public KeyPair(Algorithm alg) throws KeyPairGenerationException
	{
		if(alg == null) throw new IllegalArgumentException("The Key pair must be generated using a specified algorithm");
		
		KeyPairGenerator generator;
		this.algorithm = alg;
		
		try
		{
			generator = KeyPairGenerator.getInstance(alg.getName(), new BouncyCastleProvider());
		}
		catch(NoSuchAlgorithmException e)
		{
			throw new KeyPairGenerationException("The " + alg.toString() + " algorithm is not supported", e).setError(Error.UNSUPPORTED_ALGORITHM);
		}
		
		if(alg.toString().startsWith(Algorithm.RSA.getName()))
		{
			generator.initialize(alg.getKeySize(), new SecureRandom());
		}
		else
		{
			ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("brainpoolp" + alg.getKeySize() + "t1");
			
			try 
			{
				generator.initialize(ecGenSpec, new SecureRandom());
			}
			catch(InvalidAlgorithmParameterException e) 
			{
				throw new KeyPairGenerationException("It was not possible to generate the EC parameters for " + alg.toString(), e).setError(Error.INVALID_ELLIPTIC_CURVE_PARAMETERS);
			}
		}
		
		java.security.KeyPair keypair = generator.generateKeyPair();
		
		this.privateKey = keypair.getPrivate();
		this.publicKey = keypair.getPublic();
	}
	
	public Algorithm getAlgorithm()
	{
		return this.algorithm;
	}
	
	public PrivateKey getPrivateKey()
	{
		return this.privateKey;
	}
	
	public PublicKey getPublicKey()
	{
		return this.publicKey;
	}
}
